package com.evil.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserService;
import com.evil.account.constant.AccountConstant;
import com.evil.account.mapper.UserMapper;
import com.evil.account.pojo.dto.user.enterprise.AddUserEnterpriseDTO;
import com.evil.account.pojo.entity.User;
import com.evil.account.pojo.entity.UserEnterprise;
import com.evil.account.service.*;
import com.evil.common.account.dto.FindByNameAndEIdReqDTO;
import com.evil.common.account.dto.FindByNamesAndEIdReqDTO;
import com.evil.common.account.dto.user.*;
import com.evil.common.account.enums.IsManageEnum;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.core.dto.FindByIdsReqDTO;
import com.evil.common.core.entity.QueryModel;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.PasswordUtil;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.file.dto.FileRespDTO;
import com.evil.file.api.RemoteFileService;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 用户表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService, RemoteUserService {

    private final LoginUtil loginUtil;

    private final RemoteFileService remoteFileService;

    private final TokenService tokenService;

    private final UserEnterpriseService userEnterpriseService;

    private final UserDepartmentService userDepartmentService;

    private final UserRoleService userRoleService;

    private final AccountLogServiceImpl accountLogService;

    /**
     * 通过用户id查询用户基础信息
     *
     * @param userId userId
     * @return user
     */
    @Override
    public UserBaseRespDTO findBaseById(Long userId) {
        return this.baseMapper.findBaseById(userId);
    }

    /**
     * 通过用户id查询用户基础信息
     *
     * @param userId userId
     * @return user
     */
    @Override
    public UserBaseRespDTO findNormalBaseById(Long userId) {
        return this.baseMapper.findNormalBaseById(userId);
    }

    /**
     * 通过用户name查询用户基础信息
     *
     * @param findByNameAndEIdReqDTO findByNameAndEIdReqDTO
     * @return user
     */
    @Override
    public UserBaseRespDTO findBaseByName(FindByNameAndEIdReqDTO findByNameAndEIdReqDTO) {
        return this.findBaseByName(findByNameAndEIdReqDTO);
    }

    /**
     * 通过用户ids查询用户基础信息
     *
     * @param findByIdsReqDTO findByIdsReqDTO
     * @return user
     */
    @Override
    public List<UserBaseRespDTO> findBaseByIds(FindByIdsReqDTO findByIdsReqDTO) {
        if (findByIdsReqDTO.getCache()) {
            return this.baseMapper.findBaseByIds_Cache(findByIdsReqDTO.getIds(), findByIdsReqDTO.getFilterDeleted());
        } else {
            return this.baseMapper.findBaseByIds(findByIdsReqDTO.getIds(), findByIdsReqDTO.getFilterDeleted());
        }
    }

    /**
     * 通过用户name查询用户基础信息
     *
     * @param findByNamesAndEIdReqDTO findByNamesAndEIdReqDTO
     * @return user
     */
    @Override
    public List<UserBaseRespDTO> findBaseByNames(FindByNamesAndEIdReqDTO findByNamesAndEIdReqDTO) {
        return this.findBaseByNames(findByNamesAndEIdReqDTO);
    }

    /**
     * 通过用户name查询用户基础信息
     *
     * @param findByNamesAndEIdReqDTO findByNamesAndEIdReqDTO
     * @return user
     */
    @Override
    public Map<String, UserBaseRespDTO> findBaseMapByNames(FindByNamesAndEIdReqDTO findByNamesAndEIdReqDTO) {
        return StreamUtil.toOrderlyMapK(this.findBaseByNames(findByNamesAndEIdReqDTO), UserBaseRespDTO::getNickName);
    }

    /**
     * 通过用户ids查询用户基础信息
     *
     * @param findByIdsReqDTO findByIdsReqDTO
     * @return user
     */
    @Override
    public Map<Long, UserBaseRespDTO> findBaseMapByIds(FindByIdsReqDTO findByIdsReqDTO) {
        return StreamUtil.toMapK(this.findBaseByIds(findByIdsReqDTO), UserBaseRespDTO::getUserId);
    }

    /**
     * 修改登录用户基础信息
     *
     * @param userBaseReqDTO userBaseReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void modifyBaseInfo(UserBaseReqDTO userBaseReqDTO) {
        User user = this.baseMapper.findById(loginUtil.getLoginUserId());

        user.setEmail(userBaseReqDTO.getEmail());
        user.setNickName(userBaseReqDTO.getNickName());

        accountLogService.saveAndCheck(this.baseMapper, User::getUserId, user);

        // 修改登录用户基础信息
        userEnterpriseService.modifyBaseInfo(userBaseReqDTO);
    }

    /**
     * 新增用户
     *
     * @param addUserReqDTO addUserReqDTO
     */
    @Override
    public void add(AddUserReqDTO addUserReqDTO) {
        Long enterpriseId = loginUtil.getParamEnterpriseId(addUserReqDTO.getEnterpriseId());

        // 通过手机号查询用户信息 没有 则创建用户
        User user = this.baseMapper.isExistByTel(addUserReqDTO.getTel()).orElseGet(() -> this.createdUser(addUserReqDTO));

        AddUserEnterpriseDTO addUserEnterpriseDTO = new AddUserEnterpriseDTO(enterpriseId, user.getUserId());
        addUserEnterpriseDTO.setUserCode(addUserReqDTO.getUserCode());
        // 创建用户企业表信息
        userEnterpriseService.addUserEnterprise(addUserEnterpriseDTO);

        // 部门不为空增加 用户-部门信息
        if (CollectionUtil.isNotEmpty(addUserReqDTO.getDepartments())) {
            userDepartmentService.modify(enterpriseId, user.getUserId(), addUserReqDTO.getDepartments());
        }

        // 职务不为空增加用户-职务信息
        if (CollectionUtil.isNotEmpty(addUserReqDTO.getRoleIds())) {
            userRoleService.modify(enterpriseId, user.getUserId(), addUserReqDTO.getRoleIds());
        }
    }

    /**
     * 用户编辑
     *
     * @param modifyUserReqDTO modifyUserReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void modify(ModifyUserReqDTO modifyUserReqDTO) {
        modifyUserReqDTO.setEnterpriseId(loginUtil.getParamEnterpriseId(modifyUserReqDTO.getEnterpriseId()));
        Long enterpriseId = modifyUserReqDTO.getEnterpriseId();
        Long userId = modifyUserReqDTO.getUserId();

        // 检查企业管理员
        userEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        // 获取用户
        User user = this.baseMapper.findById(modifyUserReqDTO.getUserId());

        // 检查用户是否属于当前登录企业
        UserEnterprise userEnterprise = userEnterpriseService.isExistByEIdAndUId(enterpriseId, userId)
                .orElseThrow(() -> new BusinessException(RCodeEnum.ACCESS_DENIED));
        loginUtil.checkEnterpriseId(userEnterprise.getEnterpriseId());

        this.modfy(modifyUserReqDTO, user, false);
    }

    /**
     * 修改个人信息
     *
     * @param modifyUserReqDTO modifyUserReqDTO
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void personalCenterModify(ModifyUserReqDTO modifyUserReqDTO) {
        Long userId = modifyUserReqDTO.getUserId();
        if (!loginUtil.getLoginUserId().equals(userId)) {
            throw new BusinessException(RCodeEnum.ACCESS_DENIED);
        }

        // 获取用户
        User user = this.baseMapper.findById(userId);

        this.modfy(modifyUserReqDTO, user, true);
    }

    /**
     * 获取用户列表
     *
     * @param queryModel queryModel
     * @return PageInfo
     */
    @Override
    public PageInfo<UserPageRespDTO> page(QueryModel<UserPageReqDTO> queryModel) {
        UserPageReqDTO param = queryModel.getParam();
        // 参数中企业id不存在,则获取当前登录用户的企业id ; 存在则说明是总后台调用
        Long enterpriseId = loginUtil.getParamEnterpriseId(param.getEnterpriseId());
        param.setEnterpriseId(enterpriseId);
        PageInfo<UserPageRespDTO> userPage = this.baseMapper.userPage(queryModel);

        userPage.getList().forEach(u -> {
            // 获取头像url8
            Optional.ofNullable(u.getPortraitFileId()).flatMap(remoteFileService::isExistBaseByFileId)
                    .ifPresent(f -> u.setPortraitFileUrl(f.getFileUrl()));
            // 部门主管
            boolean isManage = u.getDepartments().stream().anyMatch(d -> IsManageEnum.MANAGE.getId() == d.getIsManage());
            u.setIsManage(isManage ? IsManageEnum.MANAGE.getId() : IsManageEnum.UN_MANAGE.getId());
        });

        return userPage;
    }

    /**
     * 获取用户基础信息分页列表
     *
     * @param queryModel queryModel
     * @return PageInfo
     */
    @Override
    public PageInfo<UserBaseRespDTO> basePage(QueryModel<UserPageReqDTO> queryModel) {
        UserPageReqDTO param = queryModel.getParam();
        // 参数中企业id不存在,则获取当前登录用户的企业id ; 存在则说明是总后台调用
        Long enterpriseId = loginUtil.getParamEnterpriseId(param.getEnterpriseId());
        param.setEnterpriseId(enterpriseId);
        PageInfo<UserBaseRespDTO> userPage = this.baseMapper.userBasePage(queryModel);

        userPage.getList().forEach(u -> {
            // 获取头像url8
            Optional.ofNullable(u.getPortraitFileId()).flatMap(remoteFileService::isExistBaseByFileId)
                    .ifPresent(f -> u.setPortraitFileUrl(f.getFileUrl()));
        });
        return userPage;
    }

    /**
     * 个人中心
     *
     * @param userDetailReqDTO userDetailReqDTO
     * @return UserDetailRespDTO
     */
    @Override
    public UserDetailRespDTO userDetail(UserDetailReqDTO userDetailReqDTO) {
        Long enterpriseId = loginUtil.getParamEnterpriseId(userDetailReqDTO.getEnterpriseId());
        Long userId = loginUtil.getParamUserId(userDetailReqDTO.getUserId());
        // 验证用户是否存在
        User user = this.baseMapper.findById(userId);
        UserDetailRespDTO userDetail = BeanUtil.copyProperties(user, UserDetailRespDTO.class);

        // 获取-用户企业信息
        userEnterpriseService.isExistByEIdAndUId(enterpriseId, user.getUserId())
                .ifPresent(userEnterprise -> {
                    userDetail.setIsEnable(userEnterprise.getIsEnable());
                    userDetail.setUserCode(userEnterprise.getUserCode());
                });

        // 获取-头像
        Optional.ofNullable(userDetail.getPortraitFileId()).flatMap(remoteFileService::isExistBaseByFileId)
                .ifPresent(f -> userDetail.setPortraitFileUrl(f.getFileUrl()));

        UserEnterpriseDTO userEnterpriseDTO = new UserEnterpriseDTO(enterpriseId, userId);
        // 获取-部门
        userDetail.setDepartments(userDepartmentService.findUserDepartmentBaseByUserEnterprise(userEnterpriseDTO));
        // 获取-职务
        userDetail.setRoles(userRoleService.userRoleBases(userEnterpriseDTO));

        return userDetail;
    }

    /**
     * 修改头像地址
     *
     * @param modifyPortraitReqDTO modifyUserPortraitReqDTO
     */
    @Override
    public void modifyUserPortrait(ModifyPortraitReqDTO modifyPortraitReqDTO) {
        // 修改别人，需要管理员
        if (null != modifyPortraitReqDTO.getUserId()) {
            userEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());
        }
        Long enterpriseId = loginUtil.getParamEnterpriseId(modifyPortraitReqDTO.getEnterpriseId());
        Long userId = loginUtil.getParamUserId(modifyPortraitReqDTO.getUserId());

        User user = this.baseMapper.findById(userId);
        FileRespDTO fileRespDTO = verifyFile(modifyPortraitReqDTO.getPortraitFileCode(), enterpriseId);

        user.setPortraitFileId(fileRespDTO.getFileId());

        accountLogService.saveAndCheck(this.baseMapper, User::getUserId, user);
    }

    /**
     * 修改用户密码
     *
     * @param modifyPasswordReqDTO updatePasswordReqDTO
     */
    @Override
    public void modifyPassword(ModifyPasswordReqDTO modifyPasswordReqDTO) {
        // 修改别人，需要管理员
        if (null != modifyPasswordReqDTO.getUserId()) {
            userEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());
        }
        Long userId = loginUtil.getParamUserId(modifyPasswordReqDTO.getUserId());

        User user = this.baseMapper.findById(userId);
        String password = PasswordUtil.getSecretPassword(modifyPasswordReqDTO.getPassword().trim(), user.getSalt());
        if (!user.getPassword().equalsIgnoreCase(password)) {
            throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "旧密码错误，请重新输入");
        }
        String newPassword = PasswordUtil.getSecretPassword(modifyPasswordReqDTO.getNewPassword().trim(), user.getSalt());
        user.setPassword(newPassword);
        accountLogService.saveAndCheck(this.baseMapper, User::getUserId, user);
        tokenService.delUserToken(user.getUserId());
    }

    /**
     * 重置用户密码
     *
     * @param resetPasswordReqDTO resetPasswordReqDTO
     */
    @Override
    public String resetPassword(ResetPasswordReqDTO resetPasswordReqDTO) {
        loginUtil.getParamEnterpriseId(resetPasswordReqDTO.getEnterpriseId());
        // 校验用户是否存在
        User user = this.baseMapper.findById(resetPasswordReqDTO.getUserId());
        // 生成用户重置后的密码
        String string = RandomUtil.randomString(8);
        String password = PasswordUtil.getSecretPassword(string, user.getSalt());
        user.setPassword(password);
        // 保存数据
        accountLogService.saveAndCheck(this.baseMapper, User::getUserId, user);
        tokenService.delUserToken(user.getUserId());
        return string;
    }

    /**
     * 获取用户数量
     *
     * @param userCountReqDTO enterpriseId
     * @return userAmountRespDTO
     */
    @Override
    public UserCountRespDTO userCount(UserCountReqDTO userCountReqDTO) {
        Long enterpriseId = loginUtil.getParamEnterpriseId(userCountReqDTO.getEnterpriseId());

        UserCountRespDTO userAmount = new UserCountRespDTO();
        userAmount.setUserAll(this.baseMapper.findUserCount(enterpriseId, null, null));
        userAmount.setDisableUser(this.baseMapper.findUserCount(enterpriseId, SwitchEnum.NO.getId(), null));
        userAmount.setEnableUser(this.baseMapper.findUserCount(enterpriseId, SwitchEnum.YES.getId(), null));
        userAmount.setUnallocated(this.baseMapper.findUserCount(enterpriseId, SwitchEnum.YES.getId(), true));

        return userAmount;
    }

    private void modfy(ModifyUserReqDTO modifyUserReqDTO, User user, boolean personal) {
        Long enterpriseId = modifyUserReqDTO.getEnterpriseId();
        Long userId = modifyUserReqDTO.getUserId();
        // 手机号禁止修改
        if (!modifyUserReqDTO.getTel().equals(user.getTel())) {
            throw new BusinessException(RCodeEnum.USER_CANT_MODIFY_TEL);
        }
        // 获取用户企业
        UserEnterprise userEnterprise = userEnterpriseService.findByEIdAndUId(enterpriseId, userId);

        // 个人中心才会修改自身的昵称
        List<String> ignoreFields = new ArrayList<>();
        if (!personal) {
            ignoreFields.add(User.COL_NICK_NAME);
        }
        BeanUtil.copyProperties(modifyUserReqDTO, user, ignoreFields.toArray(new String[]{}));

        // 头像
        if (StringUtils.isNotBlank(modifyUserReqDTO.getFileCode())) {
            FileRespDTO fileRespDTO = verifyFile(modifyUserReqDTO.getFileCode(), enterpriseId);
            user.setPortraitFileId(fileRespDTO.getFileId());
        }
        accountLogService.saveAndCheck(this.baseMapper, User::getUserId, user);

        // 保存用户企业信息
        userEnterprise.setUserCode(modifyUserReqDTO.getUserCode());
        userEnterprise.setNickName(modifyUserReqDTO.getNickName());
        accountLogService.saveAndCheck(userEnterpriseService.getBaseMapper(), UserEnterprise::getUserEnterpriseId, userEnterprise);

        // 保存用户角色
        userRoleService.modify(enterpriseId, userId, modifyUserReqDTO.getRoleIds());
        // 保存用户部门
        userDepartmentService.modify(enterpriseId, userId, modifyUserReqDTO.getDepartments());
    }

    /**
     * 校验过的参数进行create
     *
     * @param addUserReqDTO 参数
     */
    private User createdUser(AddUserReqDTO addUserReqDTO) {
        User user = new User();
        BeanUtil.copyProperties(addUserReqDTO, user);
        if (StringUtils.isNotBlank(addUserReqDTO.getFileCode())) {
            remoteFileService.isExistBaseByFileCode(addUserReqDTO.getFileCode()).ifPresent(f -> user.setPortraitFileId(f.getFileId()));
        }
        user.setSalt(PasswordUtil.getSalt());
        user.setUserType(0L);
        // 密码为空则获取默认密码,否则拿到用户输入密码 加密后持久化
        String password = StringUtils.defaultIfBlank(addUserReqDTO.getPassword(), AccountConstant.DEFAULT_PASSWORD);
        user.setPassword(PasswordUtil.getSecretPassword(password, user.getSalt()));
        accountLogService.saveAndCheck(this.baseMapper, User::getUserId, user);

        return user;
    }

    private FileRespDTO verifyFile(String fileCode, Long enterpriseId) {
        FileRespDTO fileRespDTO = remoteFileService.findBaseByFileCode(fileCode);
        loginUtil.checkEnterpriseId(fileRespDTO.getEnterpriseId(), enterpriseId);
        return fileRespDTO;
    }
}
